// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AsyncUtility.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the AsyncUtility type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Testing.Threading
{
    using System;
    using System.Diagnostics;
    using System.Threading;

    public static class AsyncUtility
    {
        private static int _defaultTimeout = 2000;

        #region Fields

        public static int DefaultTimeout
        {
            get
            {
                if (Debugger.IsAttached)
                {
                    return Int32.MaxValue;
                }

                return _defaultTimeout;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException("value", value, "Value cannot be less than zero.");
                }

                _defaultTimeout = value;
            }
        }

        #endregion

        public static void InvokeAndWait(Action<ManualResetEvent> action)
        {
            InvokeAndWait(action, DefaultTimeout);
        }

        public static void InvokeAndWait(Action<ManualResetEvent> action, int timeout)
        {
            var resetEvent = new ManualResetEvent(false);
            action(resetEvent);
            if (!resetEvent.WaitOne(timeout))
            {
                throw new TimeoutException();
            }
        }

        public static void InvokeAsyncAndWait(Action<ManualResetEvent> action)
        {
            InvokeAsyncAndWait(action, DefaultTimeout);
        }

        public static void InvokeAsyncAndWait(Action<ManualResetEvent> action, int timeout)
        {
            InvokeAndWait(resetEvent => ThreadPool.QueueUserWorkItem(s => action(resetEvent), timeout));
        }

        public static void InvokeAsyncAndWait(Action action)
        {
            InvokeAsyncAndWait(action, DefaultTimeout);
        }

        public static void InvokeAsyncAndWait(Action action, int timeout)
        {
            InvokeAsyncAndWait(resetEvent =>
               {
                   action();
                   resetEvent.Set();
               }, timeout);
        }

        public static void InvokeAndSleepUntil(Action action, Func<bool> condition)
        {
            InvokeAndSleepUntil(action, condition, DefaultTimeout);
        }

        public static void InvokeAndSleepUntil(Action action, Func<bool> condition, int timeout)
        {
            action();
            SleepUntil(condition, timeout);
        }

        public static void SleepUntilThreadIsInState(Thread thread, System.Threading.ThreadState state)
        {
            SleepUntil(() => (thread.ThreadState & state) == state);
        }

        public static void SleepUntil(Func<bool> condition)
        {
            SleepUntil(condition, DefaultTimeout);
        }

        public static void SleepUntil(Func<bool> condition, int timeout)
        {
            var sw = new Stopwatch();
            sw.Start();
            while (sw.ElapsedMilliseconds < timeout)
            {
                if (condition())
                {
                    return;
                }
                Thread.Sleep(0);
            }
            throw new TimeoutException();
        }
    }
}